/*
 * direccionamiento.c
 *
 *  Created on: 03/10/2011
 *      Author: utn_so
 */

#include "libPFS.h"

uint32_t obtenerPrimerSectorDataRegion ()
{
  uint32_t primerSectorData;
  extern stBootSector sectorBoot;
  primerSectorData = sectorBoot.BPB_RsvdSecCnt
      + sectorBoot.BPB_NumFATs * sectorBoot.BPB_FATSz32;

  return primerSectorData;
}

uint32_t obtenerPrimerSectorDeUnCluster (uint32_t nroCluster)
{
  uint32_t iPrimerSectorDelCluster, primerSectorDataRegion;
  extern stBootSector sectorBoot;
  primerSectorDataRegion = obtenerPrimerSectorDataRegion ();
  iPrimerSectorDelCluster = ( (nroCluster - sectorBoot.BPB_RootClus)
      * sectorBoot.BPB_SecPerClus) + primerSectorDataRegion;

  return iPrimerSectorDelCluster;
}

uint32_t *deClusterASectores (uint32_t nroCluster)
{
  uint32_t *sectoresDelCluster;
  uint32_t i = 0;
  uint32_t primerSector;
  extern stBootSector sectorBoot;

  sectoresDelCluster = ( uint32_t *) malloc (
      sectorBoot.BPB_SecPerClus * sizeof(uint32_t));
  primerSector = obtenerPrimerSectorDeUnCluster (nroCluster);
  while (i < sectorBoot.BPB_SecPerClus)
    {
      sectoresDelCluster [i] = primerSector + i;
      i++;
    }
  return sectoresDelCluster;
}

uint32_t deSectorACluster (uint32_t nroSector)
{
  uint32_t cluster;
  uint32_t primerSectorDataRegion;
  primerSectorDataRegion = obtenerPrimerSectorDataRegion ();

  extern stBootSector sectorBoot;
  cluster = ( (nroSector - primerSectorDataRegion) / sectorBoot.BPB_SecPerClus)
      + sectorBoot.BPB_RootEntCnt;
  return cluster;
}
uint32_t calcularPosicion (uint32_t nroSector)
{
  extern t_config archivoCfg;
  div_t divisor;
  divisor = div (nroSector, archivoCfg.tamanioBloque);

  return (divisor.rem) * TAMSECTOR;
}

uint32_t deClusterABloque (uint32_t nroCluster)
{
  uint32_t primerSecCluster;
  extern t_config archivoCfg;
  primerSecCluster = obtenerPrimerSectorDeUnCluster (nroCluster);
  return primerSecCluster / archivoCfg.tamanioBloque;
}
uint32_t obtenerPrimerSectorDeUnBloque (uint32_t nroBloque)
{
  extern t_config archivoCfg;
  return nroBloque * archivoCfg.tamanioBloque;

}

uint32_t cantidadClustersDelBloque ()
{
  extern stBootSector sectorBoot;
  extern t_config archivoCfg;
  uint32_t cantidadCluster;
  cantidadCluster = sectorBoot.BPB_SecPerClus / archivoCfg.tamanioBloque;

  return cantidadCluster;
}

void liberarConexion (int32_t *nroSocket)
{
  extern t_config archivoCfg;
  extern stPoolConMutex stpoolConexiones;
  int32_t bloqueaSocket = 0,cantLockeadas=0;
  int32_t buscoConexion = 0;
  while (buscoConexion < archivoCfg.cantidadConexiones && cantLockeadas < archivoCfg.tamanioBloque)
    {
       if (stpoolConexiones.poolConexiones [buscoConexion].nroConexion == nroSocket[cantLockeadas])
        {
            bloqueaSocket = pthread_mutex_unlock (&(stpoolConexiones.poolConexiones [buscoConexion].mutex));
            cantLockeadas++;
        }

      buscoConexion++;
    }
}
void buscarConexionLibre (int32_t **buscoConexion)
{
  extern t_config archivoCfg;
  int32_t bloquearPoll = 0, recorroPool = 0, bloqueaSocket = 0,
      conexionEncontrada = 0;
  extern stPoolConMutex stpoolConexiones;

  bloquearPoll = pthread_mutex_trylock (&stpoolConexiones.mutexPool); //Si El pool esta libre Bloqueo y obtengo todas las conexiones
  if (bloquearPoll != EBUSY)
    {
      while (conexionEncontrada < archivoCfg.tamanioBloque && recorroPool < archivoCfg.cantidadConexiones)
      {
          bloqueaSocket = pthread_mutex_trylock (&stpoolConexiones.poolConexiones [recorroPool].mutex);
          if (bloqueaSocket != EBUSY)
            {
              (*buscoConexion) [conexionEncontrada] = stpoolConexiones.poolConexiones [recorroPool].nroConexion;
              conexionEncontrada++;
            }
          recorroPool++;
      }
      bloquearPoll = pthread_mutex_unlock (&stpoolConexiones.mutexPool);
    }

}

void armarCluster (uint32_t nroSector, char *dataEscritura, char **cluster)
{
  uint32_t cantidadBloques;
  extern t_config archivoCfg;
  cantidadBloques = cantidadClustersDelBloque ();

  int16_t tamBloque = archivoCfg.tamanioBloque * TAMSECTOR;
  char *pBloqueFisico = NULL;
  int32_t i = 0,escrituraOk;

  if (dataEscritura == NULL)
    *cluster = ( char *) calloc (cantidadBloques, archivoCfg.tamanioBloque * TAMSECTOR);
  while (i < cantidadBloques)
    {
      if (dataEscritura == NULL)
        {
          enviarPeticiones (nroSector,&pBloqueFisico);
          memcpy ((*cluster) + (i * tamBloque), pBloqueFisico, tamBloque);
          free (pBloqueFisico);
          pBloqueFisico = NULL;
          i++;
        }
      else
        {
          escrituraOk = enviarPeticionesEscritura(nroSector,dataEscritura + (tamBloque *i));
          *cluster = NULL;
          i++;
        }

      nroSector += archivoCfg.tamanioBloque;

    }



}
void enviarPeticiones (uint32_t nroSector,char **bloque)
{
  extern t_config archivoCfg;
  int32_t *nroSocket;
  int32_t cantEnviados = 0, cantLeidos = 0, msjEnviado = 0 ,cantConexiones=0;
  t_NIPC mspSectorAEnviar, nipcRecibido;
  int32_t conexionesOcupadas = 0,j = 0, dataRecibida = 0,posicion=0;
  memset (&nipcRecibido, '\0', sizeof (nipcRecibido));

  if (archivoCfg.cantidadConexiones < archivoCfg.tamanioBloque)
    {
    nroSocket = ( int32_t *) calloc (archivoCfg.cantidadConexiones, sizeof(int32_t));
    cantConexiones = archivoCfg.cantidadConexiones;
    }
  else
    {
    nroSocket = ( int32_t *) calloc (archivoCfg.tamanioBloque, sizeof(int32_t));
    cantConexiones = archivoCfg.tamanioBloque;
    }

  *bloque = ( char *) calloc (archivoCfg.tamanioBloque, TAMSECTOR);

  while(cantLeidos < archivoCfg.tamanioBloque)
    {

      buscarConexionLibre (&nroSocket);
      conexionesOcupadas = 0;
      while (cantEnviados < archivoCfg.tamanioBloque && cantConexiones > conexionesOcupadas )
      {
        NIPC_crearpaquete (NULL, LEERSECTOR, 0, NULL,nroSector,&mspSectorAEnviar);
        msjEnviado = NIPC_enviar (nroSocket [conexionesOcupadas],mspSectorAEnviar);
        NIPC_destruirpaquete (&mspSectorAEnviar);
        nroSector++;
        cantEnviados++;
        conexionesOcupadas++;
      }

      j=0;
      while (j < conexionesOcupadas)
      {
        dataRecibida = NIPC_recibir (nroSocket [j], &nipcRecibido);
        cantLeidos++;
        posicion = calcularPosicion (nipcRecibido.nroSector);
        memcpy ((*bloque) + posicion,nipcRecibido.payload, nipcRecibido.PayloadLength);
        NIPC_destruirpaquete (&nipcRecibido);
        j++;
      }
      liberarConexion(nroSocket);

    }
  free (nroSocket);

}

int32_t enviarPeticionesEscritura (uint32_t nroSector, char *dataEscritura)
{
  extern t_config archivoCfg;
  int32_t *nroSocket;
  int32_t cantEnviados = 0,msjEnviado = 0,dataRecibida=0,conexionesOcupadas=0;
  t_NIPC mspSectorAEnviar, nipcRecibido;
  int32_t posicion=0,cantEscritos=0,j=0,cantConexiones=0;
  memset (&nipcRecibido, '\0', sizeof (nipcRecibido));

  if (archivoCfg.cantidadConexiones < archivoCfg.tamanioBloque)
  {
     nroSocket = ( int32_t *) calloc (archivoCfg.cantidadConexiones, sizeof(int32_t));
     cantConexiones = archivoCfg.cantidadConexiones;
  }
   else
   {
     nroSocket = ( int32_t *) calloc (archivoCfg.tamanioBloque, sizeof(int32_t));
     cantConexiones = archivoCfg.tamanioBloque;
   }
  while(cantEnviados < archivoCfg.tamanioBloque)
    {
      buscarConexionLibre (&nroSocket);
      conexionesOcupadas=0;
      while (cantEnviados < archivoCfg.tamanioBloque && cantConexiones > conexionesOcupadas)
      {
        posicion = calcularPosicion (nroSector);
        NIPC_crearpaquete (NULL, ESCRIBIRSECTOR,TAMSECTOR,dataEscritura + posicion,nroSector,&mspSectorAEnviar);
        msjEnviado = NIPC_enviar (nroSocket [conexionesOcupadas],mspSectorAEnviar);
        NIPC_destruirpaquete (&mspSectorAEnviar);
        nroSector++;
        cantEnviados++;
        conexionesOcupadas++;
      }

      j=0;
      while (j < conexionesOcupadas)
      {

        dataRecibida = NIPC_recibir (nroSocket [j], &nipcRecibido);
        if(dataRecibida != 0)
          	NIPC_destruirpaquete (&nipcRecibido);

        cantEscritos++;
        j++;
      }

      liberarConexion(nroSocket);
    }
  free (nroSocket);

return 0;
}
void CrearPoolConexiones (char *pDirIp, int16_t puerto)
{
  extern t_config archivoCfg;
  int32_t pool = 0;
  int32_t socketConexion;
  extern stPoolConMutex stpoolConexiones;
  stpoolConexiones.poolConexiones = ( stPoolMutex *) calloc (
      archivoCfg.cantidadConexiones, sizeof(stPoolMutex));
  while (pool < archivoCfg.cantidadConexiones)
    {
      socketConexion = AFINET_conectar (pDirIp, puerto);
      if (socketConexion == -1) // Loguear Error
        exit(0);
      if (pool == 0)
        handshake(socketConexion);
      stpoolConexiones.poolConexiones [pool].nroConexion = socketConexion;
      pthread_mutex_init (&stpoolConexiones.poolConexiones [pool].mutex, NULL);
      pool++;
    }
}
